Building Graphs in wc_rules

In wc_rules, we are essentially constructing graph representations of chemical entities. The basic building blocks of the graph are

  • nodes, which include instances of molecules, sites and interactions, and,
  • edges, which are bidirectional relations between molecules, sites and interactions.

To build this graph, one needs to know about :

  • subclasses
  • instances
  • relations
  • instance attribute methods
  • class attributes

Subclasses

Objects such as molecules, sites and interactions are derived from generic base classes provided in wc_rules. To create various types of these objects, one can simply subclass them as often as needed.

Below, we create the type hierarchy

  • Molecule -> A -> A1

where A is a subclass of molecule and A1 is a subclass of A


In [1]:
from wc_rules.chem2 import Molecule,Site

class A(Molecule):
    pass

class A1(A):
    pass

Instances

Instances are created by calling the constructor of the class. These constitute the nodes of the graph.


In [2]:
a1_001 = A1()
a1_001


Out[2]:
<__main__.A1 at 0x22eea9cb588>

Instances typically have scalar attributes that can be set during construction. For example, all instances have an id attribute.


In [3]:
a1_001 = A1(id='instance_001_of_A1')
a1_001.id


Out[3]:
'instance_001_of_A1'

Instances can be type-checked against any parent class.


In [4]:
isinstance(a1_001,A1) and isinstance(a1_001,A) and isinstance(a1_001,Molecule)


Out[4]:
True

Relations

Instances have relations to each other, which constitute the edges of the graph. Relations are managed using pairs of attributes on each of the respective classes. For example, Molecule has a sites attribute that holds a list of site instances. Site has a molecule attribute that holds a single instance of a molecule.


In [5]:
m,s = Molecule(), Site()
m.sites, s.molecule


Out[5]:
([], None)

In [6]:
m.sites.append(s)
m.sites


Out[6]:
[<wc_rules.chem2.Site at 0x22eea9cbd30>]

In [7]:
s.molecule


Out[7]:
<wc_rules.chem2.Molecule at 0x22eea9cbd68>

The convention for attribute name is typically to use a singular name such as molecule if it refers to a single instance or a plural name such as sites if it refers to a list of instances.

Instance Attribute Methods

wc_rules provides setters, getters and unsetters for various instance attributes. For example, for the id attribute, we have get_id() and set_id(id).


In [8]:
m = Molecule()
m.set_id('m_001')
m.get_id()


Out[8]:
'm_001'

Typically, the following naming conventions are followed for getters, setters and unsetters:

Attribute type Getter Setter Unsetter
Scalar get_* set_*
Object reference get_* set_* unset_*
List of object references get_* add_* remove_*

Setters and unsetters always "return self", so they can be chained indefinitely. For example,


In [9]:
m = Molecule().set_id('m_001').add_sites( Site(), Site() )
m.sites


Out[9]:
[<wc_rules.chem2.Site at 0x22eea9e5278>,
 <wc_rules.chem2.Site at 0x22eea9e52b0>]

Additional points to remember about instance attribute methods:

  • Each class may have additional inbuilt getters and setters with custom functionality.
  • The methods are inherited during subclassing.
  • Setters managing class relations can have redundant functionality, e.g., Molecule.add_sites() and Site.set_molecule() will have the same effect of establishing relations between molecules and sites.

Class Attributes

Class attributes dictate behavior of a class and can be modified during subclassing. For example, the Site method has the following class attributes:

  • allowed_to_bind, a Boolean that indicates whether a bond is allowed on that site type,
  • allowed_molecule_types, a tuple of Molecule classes that are allowed to be related to that site.

Below, we

  • create Molecule subclasses A and B,
  • create Site subclasses X1, X2, Y and Z,
  • restrict X1 and X2 to only be related to A,
  • restrict Y and Z to only be related to B,
  • disallow binding on Z

In [10]:
class A(Molecule):pass

class B(Molecule):pass

class X1(Site):
    allowed_site_types = (A,)

class X2(Site):
    allowed_site_types = (A,)

class Y(Site):
    allowed_site_types = (B,)

class Z(Site):
    allowed_site_types = (B,)
    allowed_to_bind = False